/* @flow */

import { ASSET_TYPES } from 'shared/constants'
import { defineComputed, proxy } from '../instance/state'
import { extend, mergeOptions, validateComponentName } from '../util/index'

export function initExtend(Vue: GlobalAPI) {
  /**
   * Each instance constructor, including Vue, has a unique
   * cid. This enables us to create wrapped "child
   * constructors" for prototypal inheritance and cache them.
   */
  Vue.cid = 0
  let cid = 1

  /**
   * 通过继承创建一个组件, Vue所有的component 都是通过这个方法继承得到的构造器
   * @param {*} extendOptions 组件的配置对象
   */
  Vue.extend = function (extendOptions: Object): Function {
    //组件的配置数据对象{ data(){ return { message: '123456' } }  }
    extendOptions = extendOptions || {}
    const Super = this
    const SuperId = Super.cid

    //生成后的组件里面有个隐藏属性_Ctor就是保存的组件实例，这里使用了缓存，首次不存在缓存
    //二次直接走此路线，就不用再进行下面的步骤，小优化
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }

    //如果我们全程所有的配置都不提供name,这里是undefined
    const name = extendOptions.name || Super.options.name
    if (process.env.NODE_ENV !== 'production' && name) {
      validateComponentName(name)
    }

    const Sub = function VueComponent(options) {
      this._init(options)
    }

    /**
     * 继承构造器，这里Super = Vue.prototype , 属性如下：
     * $delete, $destroy, $emit, $forceUpdate, $mount, $nextTick, $off, $on, $once, $set, $watch
     * _b, _d, _e, _f, _g, _i, _k, _l, _m,_n, _o, _p, _q, _s, _t, _u, _v
     * _update, __patch__, _init: ƒ (options), _render: ƒ ()
     * $data: undefined
     * $isServer: false
     * $props: undefined
     * $ssrContext: undefined
     */
    Sub.prototype = Object.create(Super.prototype)
    // 重新指向构造器
    Sub.prototype.constructor = Sub
    
    Sub.cid = cid++
    //合并配置项
    Sub.options = mergeOptions(
      Super.options, // Vue 全局配置components, filters, directives
      extendOptions, //组件自己的配置项
    )

    Sub['super'] = Super //指向根Vue

    if (Sub.options.props) {
      //将props的属性通过劫持方式代理到 Sub.prototype上
      initProps(Sub)
    }
    if (Sub.options.computed) {
      initComputed(Sub)
    }

    // allow further extension/mixin/plugin usage
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use

    //component ,  filter , directive
    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })
    // enable recursive self-lookup
    if (name) {
      Sub.options.components[name] = Sub
    }

    // 在扩展时保留对超级选项的引用
    // 稍后在实例化时，我们可以检查Super的选项是否有更新
    Sub.superOptions = Super.options //{components， directives, filters, _base}
    Sub.extendOptions = extendOptions //component传递的配置项
    Sub.sealedOptions = extend({}, Sub.options) //component传递的配置和Vue合并后的配置项

    // cache constructor
    cachedCtors[SuperId] = Sub
    return Sub
  }
}

function initProps(Comp) {
  const props = Comp.options.props
  for (const key in props) {
    proxy(Comp.prototype, `_props`, key)
  }
}

function initComputed(Comp) {
  const computed = Comp.options.computed
  for (const key in computed) {
    defineComputed(Comp.prototype, key, computed[key])
  }
}
